#include "planes.h"
#include <string>

//-------------------------------------------------------+
// Component functions of fn 'GetPlanePoints'

// will need these functions to test whether the outputs used from Eigen 
// are the same as the outputs from ckmath

Eigen::Vector3f get_polygon_normal(const Eigen::Vector3f* vertices, const size_t nvertices) {
    Eigen::Vector3f x = {0.0f, 0.0f, 0.0f};
    for (size_t i = 0; i < nvertices; ++i) {
		const size_t kIndexA = ((nvertices - 1) + i) % nvertices;
		const size_t kIndexB = i;

		const Eigen::Vector3f& krA = vertices[kIndexA];
		const Eigen::Vector3f& krB = vertices[kIndexB];

		const Eigen::Vector3f kCrossProduct = krA.cross(krB);
        x += kCrossProduct;
    }
    x.normalize();
    return x;
}

inline Eigen::Vector3f get_center(const Eigen::Vector3f* kppoints, 
                                      const size_t npoints) {
	Eigen::Vector3f result{0.0f, 0.0f, 0.0f};
	for(size_t i = 0; i < npoints; ++i)
	{
		result += kppoints[i];
	}
    result *= (1.0f / (float)npoints);
    return result;
}

inline float get_width(const Eigen::Vector3f* kppoints,
                       const size_t npoints,
                       const Eigen::Vector3f center) {
	float width = 1.0f;
	for(size_t i = 0; i < npoints; ++i) {
        // norm() = vector magnitude in Eigen.
		width = std::max(width, (kppoints[i] - center).norm());
	}
    return width;
}

inline Eigen::Vector3f get_tangent(const Eigen::Vector3f* x, 
                                   float width) {
    Eigen::Vector3f t = (x[1] - x[0]);
    t.normalize();
    return (t *  (width * 10.0f));
}

inline Eigen::Vector3f get_bitangent(const Eigen::Vector3f& tan, 
                                     const Eigen::Vector3f& norm,
                                     float width) {
    Eigen::Vector3f x = norm.cross(tan);
    x.normalize();
    return (x * (width * 10.0f));
}

//-------------------------------------------------------+

TPlanePoints GetPlanePoints(const Eigen::Vector3f* _kpPoints, const size_t _kNumPoints)
{
	TPlanePoints PlanePoints;
	const Eigen::Vector3f kNormal  = get_polygon_normal(_kpPoints, _kNumPoints);
	const Eigen::Vector3f kCenter  = get_center(_kpPoints, _kNumPoints);
	float fWidth                   = get_width(_kpPoints, _kNumPoints, kCenter);
	const Eigen::Vector3f kTangent   = get_tangent(_kpPoints, fWidth);
	const Eigen::Vector3f kBiTangent = get_bitangent(kTangent, kNormal, fWidth);

	PlanePoints.m_A = kCenter + kBiTangent;
	PlanePoints.m_B = kCenter;
	PlanePoints.m_C = kCenter + kTangent;

	return(PlanePoints);
}

std::vector<TPlanePoints> GetBrushPlanes(const TBrush& _krBrush)
{
	std::vector<TPlanePoints> Planes;
	for(const TFace& krFace : _krBrush.m_Faces)
	{
		std::vector<Eigen::Vector3f> Verts(krFace.m_Indices.size());
		for(size_t i = 0; i < krFace.m_Indices.size(); ++i)
		{
			Verts[i] = _krBrush.m_Vertices[krFace.m_Indices[i]];
		}
		TPlanePoints PP = GetPlanePoints(Verts.data(), Verts.size());
		PP.m_material = krFace.m_Material;
        if (!krFace.hex.empty() && !krFace.m_Material.empty()) {
            PP.m_material.append("+" + krFace.hex);
        }
        PP.hscale    = krFace.m_fXScale;
        PP.hshift    = krFace.m_fXOffset;
        PP.vscale    = krFace.m_fYScale;
        PP.vshift    = krFace.m_fYOffset;
        PP.rotation  = krFace.m_fRotation;
		Planes.push_back(PP);
	}
	return(Planes);
}
